!function (t) {
    "function" == typeof define && define.amd
        ? define(t)
        : t();
}(function () {
    
    "use strict";
    var styleKey_edgeType      = "edge.type",
        styleKey_edgeGap       = "edge.gap",
        htDefaultInterface     = ht.Default.getInternal(),
        htDefault              = ht.Default,
        htDefaultUnionPoint    = htDefault.unionPoint,
        htDefaultGrow          = htDefault.grow,
        htDefaultContainsPoint = htDefault.containsPoint,
        getNodeRect            = htDefaultInterface.getNodeRect,
        intersectionLineRect   = htDefaultInterface.intersectionLineRect;
    
    //获取线的源端位置
    function getEdgeHostPosition_source(graphView, edge) {
        return htDefaultInterface.getEdgeHostPosition(graphView, edge, "source");
    }
    
    //获取线的终点位置
    function getEdgeHostPosition_target(graphView, edge) {
        return htDefaultInterface.getEdgeHostPosition(graphView, edge, "target");
    }
    
    
    /**
     *  线段的方程     *
     *  线1{点startPoint，
     *  点targetPoint}
     *  return
     //直线方程为：(b.y-a.y)x-(b.x-a.x)y+(b.x-a.x)*a.y-(b.y-a.y)*a.x=0
     // 直线方程为：Ax+By+C=0
     * **/
    function lineFunctionParam(startPoint, targetPoint) {
        return {
            A: targetPoint.y - startPoint.y,
            B: startPoint.x - targetPoint.x,
            C: targetPoint.x * startPoint.y - startPoint.x * targetPoint.y
        };
    }
    
    //判断是否是同一个点
    function isSamePoint(point1, point2) {
        return point1.x === point2.x && point1.y === point2.y;
    }
    
    //获取连线与图元的交点坐标
    function getNodeAttachPoint(node, anchor) {
        var targetPoint = {},
            position    = node.getPosition(),
            anchor1     = node.getAnchor(),
            width       = node.getWidth(),
            height      = node.getHeight();
        targetPoint.x   = position.x + (anchor.x - anchor1.x) * width;
        targetPoint.y   = position.y + (anchor.y - anchor1.y) * height;
        return targetPoint;
    }
    
    
    /****
     * @description     绘制跨越线
     @param startPoint,  起点
     @param endPoint,  终点
     @param crossType  自定义连线折点个数   1p || 3p
     */
    function makeLines(startPoint, endPoint, crossType) {
        var arrayPoints = [],
            lines       = [],
            bycross     = [];
        
        var crossPoints = [];//折点，根据起点终点的间距分隔绘制
        if ("1p" === crossType) {
            //只有一个转折点时，
            crossPoints = [
                {x: startPoint.x, y: endPoint.y}//从起点向终点 , '直角折线：垂直线->水平线'
            ];
        }
        else if ("2p" === crossType) {
            //只有2个转折点时，
            var yGap    = endPoint.y - startPoint.y,
                xGap    = endPoint.x - startPoint.x;
            var yBigger = Math.abs(xGap) < Math.abs(yGap);
            if (yBigger) {
                crossPoints =
                    [
                        {x: startPoint.x, y: startPoint.y + yGap / 2},
                        {x: endPoint.x, y: endPoint.y - yGap / 2}
                    ];
            }
            else {
                crossPoints =
                    [
                        {x: startPoint.x + xGap / 2, y: startPoint.y},
                        {x: endPoint.x - xGap / 2, y: endPoint.y}
                    ];
            }
        }
        else if ("3p" === crossType) {
            //只有三个转折点时，
            var yGap = endPoint.y - startPoint.y,
                xGap = endPoint.x - startPoint.x;
            
            crossPoints = [{x: startPoint.x, y: startPoint.y + yGap / 5},
                {x: endPoint.x - xGap / 5, y: startPoint.y + yGap / 5},
                {x: endPoint.x - xGap / 5, y: endPoint.y}
            ];
        }
        
        //起点
        arrayPoints.push(startPoint);
        //中间的转折点
        var pointSize = crossPoints.length;
        for (var crossPointIndex = 0; crossPointIndex < pointSize; crossPointIndex++) {
            arrayPoints.push(crossPoints[crossPointIndex]);
        }
        //终点
        arrayPoints.push(endPoint);
        
        
        for (var pointIndex = 0; pointIndex < arrayPoints.length - 1; pointIndex++) {
            var oneLine = {sp: arrayPoints[pointIndex], tp: arrayPoints[pointIndex + 1]};//一个线段
            if (arrayPoints[pointIndex].y === arrayPoints[pointIndex + 1].y) {
                lines.push(oneLine);//水平线
            }
            else if (arrayPoints[pointIndex].x === arrayPoints[pointIndex + 1].x) {
                bycross.push(oneLine);//垂直线
            }
        }
        return {arrayPoints: arrayPoints, lines: lines, bycross: bycross};
    }
    
    //oneLine.sp, oneLine.tp, edge, 0, crossRadius, points, segments, graphView
    
    /****
     * @description     全局判断交叉点，绘制交叉点的弧线
     @param startPoint,  sp 起点
     @param targetPoint,   tp 终点
     @param edge  当前线
     @param oneParam   oneParam 未使用的参数,
     @param  crossRadius 圆弧半径
     @param  points 当前线的点集合
     @param  segments 当前线的点操作类型集合
     @param  graphView 所在视图
     */
    function insertCirclePoint(startPoint, targetPoint, edge, oneParam, crossRadius, points, segments, graphView) {
        
        var yGap                  = targetPoint.y - startPoint.y,
            xGap                  = targetPoint.x - startPoint.x,
            edgeLineFunctionParam = lineFunctionParam(startPoint, targetPoint);
        
        var dataModel   = graphView.dm();
        //当前线的矩形位置
        var rectEdge    = htDefaultUnionPoint(getEdgeHostPosition_source(graphView, edge), getEdgeHostPosition_target(graphView, edge));
        //获取view中与当前线有交叉的线
        var crossLines  = dataModel.toDatas(function (dataItem) {
            if ("cross" !== dataItem.s("edge.type")
                || edge === dataItem)
            {
                //非翻越线、线本身，不返回
                return false;
            }
            //目标线的矩形位置
            var dataItemRect = htDefaultUnionPoint(getEdgeHostPosition_source(graphView, dataItem), getEdgeHostPosition_target(graphView, dataItem));
            var isInter      = ht.Default.intersectsRect(rectEdge, dataItemRect);//两条线是否相交
            return isInter;
        });
        var crossPoints = [];//交叉点
        //循环被交的线，只在本线的横向线与目标的纵向线有交点
        crossLines.forEach(function (oneCrossEdge) {
            //重新获取线的绘制函数，得到所有的纵向线。
            var lineParam    = makeLines(
                getNodeAttachPoint(oneCrossEdge.getSourceAgent(), oneCrossEdge.s("cross.source.anchor")),
                getNodeAttachPoint(oneCrossEdge.getTargetAgent(), oneCrossEdge.s("cross.target.anchor")),
                oneCrossEdge.s("cross.edge.type")
            );
            var bycrossLines = lineParam.bycross;//纵向线。
            bycrossLines.forEach(function (oneByCrossLine) {
                
                if (isSamePoint(oneByCrossLine.sp, startPoint)
                    || isSamePoint(oneByCrossLine.sp, targetPoint)
                    || isSamePoint(oneByCrossLine.tp, startPoint)
                    || isSamePoint(oneByCrossLine.tp, targetPoint)
                )
                {
                    //线段跟本线的首尾端点重合，不需要进行计算
                }
                else {
                    var oneBycrossLineFunctionParam = lineFunctionParam(oneByCrossLine.sp, oneByCrossLine.tp);
                    //直线方程解出交叉点
                    var crossPoint                  = {
                        x: (edgeLineFunctionParam.B * oneBycrossLineFunctionParam.C - oneBycrossLineFunctionParam.B * edgeLineFunctionParam.C)
                            / (oneBycrossLineFunctionParam.B * edgeLineFunctionParam.A - edgeLineFunctionParam.B * oneBycrossLineFunctionParam.A),
                        y: (edgeLineFunctionParam.A * oneBycrossLineFunctionParam.C - oneBycrossLineFunctionParam.A * edgeLineFunctionParam.C)
                            / (edgeLineFunctionParam.B * oneBycrossLineFunctionParam.A - edgeLineFunctionParam.A * oneBycrossLineFunctionParam.B)
                    };//oneByCrossLine   edge的交叉点
                    
                    if (isSamePoint(oneByCrossLine.sp, crossPoint)
                        || isSamePoint(oneByCrossLine.tp, crossPoint))
                    {
                        //交叉点是端点，不处理
                    }
                    else {
                        var crossRect = htDefaultUnionPoint([startPoint, targetPoint, oneByCrossLine.sp, oneByCrossLine.tp]);//两条线所在的矩形区域
                        htDefaultGrow(crossRect, -crossRadius);//缩小矩形区域---根据弧度大小，缩小矩形范围，当间隔距离过小时，不显示弧线
                        var inRect = htDefaultContainsPoint(crossRect, crossPoint);//判断point是否包含在rect的矩形区域里
                        if (inRect) {//交叉点在线上
                            var hasAdd = false;//已记录了此点,
                            crossPoints.forEach(function (onePoint) {
                                if (isSamePoint(onePoint, crossPoint)) {
                                    hasAdd = true;
                                }
                            });
                            if (!hasAdd) {
                                crossPoints.push(crossPoint);//记录交叉点
                            }
                        }
                    }
                }
                ;
            });
        });
        
        // 按绘制顺序，排列交叉点
        if (startPoint.x === targetPoint.x) {
            //水平线，根据y排列点
            targetPoint.y > startPoint.y
                ? crossPoints.sort(function (firstPoint, secondPoint) {
                    return firstPoint.y - secondPoint.y;//升序
                })
                : crossPoints.sort(function (firstPoint, secondPoint) {
                    return secondPoint.y - firstPoint.y;//降序
                });
        }
        else {
            //非水平线，根据x排列点
            targetPoint.x > startPoint.x
                ? crossPoints.sort(function (firstPoint, secondPoint) {
                    return firstPoint.x - secondPoint.x;//升序
                })
                : crossPoints.sort(function (firstPoint, secondPoint) {
                    return secondPoint.x - firstPoint.x;//降序
                });
        }
        
        //绘制交叉点的弧线段
        crossPoints.forEach(function (onePoint) {
            //使用单位向量计算距离crossRadius的点坐标
            var xOffset   = crossRadius * xGap / Math.sqrt(Math.pow(xGap, 2) + Math.pow(yGap, 2)),
                yOffset   = crossRadius * yGap / Math.sqrt(Math.pow(xGap, 2) + Math.pow(yGap, 2)),
                fromPoint = {x: onePoint.x - xOffset, y: onePoint.y - yOffset},
                endPoint  = {x: onePoint.x + xOffset, y: onePoint.y + yOffset};
            
            //使用单位法向量计算垂直距离crossRadius的点坐标
            var xOffset1 = crossRadius * -yGap / Math.sqrt(Math.pow(xGap, 2) + Math.pow(yGap, 2)),
                yOffset1 = crossRadius * xGap / Math.sqrt(Math.pow(xGap, 2) + Math.pow(yGap, 2)),
                radPoint = {x: onePoint.x + xOffset1, y: onePoint.y + yOffset1};
            
            points.add(fromPoint);
            segments.add(2);
            points.add(radPoint);
            points.add(endPoint);
            segments.add(3);
        });
    }
    
    
    // 默认样式
    ht.Default.addMethod(ht.Style, {
        "edge.cross.radius": 5,
        "cross.source.anchor": {x: .5, y: .5},
        "cross.target.anchor": {x: .5, y: .5},
        "cross.attachPoints": [{x: 1, y: .5}, {x: 0, y: .5}, {x: .5, y: 0}, {x: .5, y: 1}],
        "cross.edge.type": "3p"
    });
    
    
    /*****
     ht.Default.setEdgeType(type, func, mutual)函数可用于自定义新连线类型：
     type：字符串类型的连线类型，对应style的edge.type属性
     func：函数类型，根据传入参数(edge, gap, graphView, sameSourceWithFirstEdge)返回连线走向信息
     edge：当前连线对象
     gap：多条连线成捆时，本连线对象对应中心连线的间距
     graphView：当前对应拓扑组件对象
     sameSourceWithFirstEdge：boolean类型，该连线是否与同组的第一条连线同源
     返回值为{points: new ht.List(...), segments: new ht.List(...)}结构的连线走向信息，segments可取值如下：
     1: moveTo，占用1个点信息
     2: lineTo，占用1个点信息
     3: quadraticCurveTo，占用2个点信息
     4: bezierCurveTo，占用3个点信息
     5: closePath，不占用点信息
     mutual：该参数决定连线是否影响起始或结束节点上的所有连线，默认为false代表只影响同source和target的EdgeGroup中的连线，
     ****/
    
    
    // edge.type : cross 扩展翻越线类型，横线翻越竖线
    // {
    //     // 连线类型
    //     "edge.type":'cross',
    //     // 翻越半径
    //     "edge.cross.radius": 5,
    //     // 翻越线原锚点
    //     "cross.source.anchor": { x: 0.5, y: 0.5 },
    //     // 翻越线目标锚点
    //     "cross.target.anchor": { x: 0.5, y: 0.5 },
    //     // 翻越线，自定义连线折点个数    1p||3p
    //     "cross.edge.type": "3p"
    // }
    ht.Default.setEdgeType("cross",
        function (edge, gap, graphView, sameSourceWithFirstEdge) {
            
            var startPoint  = getNodeAttachPoint(edge.getSourceAgent(), edge.s("cross.source.anchor")),
                endPoint    = getNodeAttachPoint(edge.getTargetAgent(), edge.s("cross.target.anchor")),
                points      = new ht.List,
                segments    = new ht.List,
                crossRadius = (new ht.List, edge.s("edge.cross.radius"));
            
            var lineToCenter = edge.s("edge.center"),//是否连到中心
                rathoRadio   = edge.s("edge.ortho"),//转折处比例
                crossType    = edge.s("cross.edge.type");//自定义连线折点个数
            
            //线的第一个点
            points.add(startPoint);
            segments.add(1);
            
            var lineInfo = makeLines(startPoint, endPoint, crossType);
            var lines    = lineInfo.lines;//水平线
            lines.forEach(function (oneLine) {
                points.add(oneLine.sp);
                segments.add(2);
                
                //为水平线插入交叉点上的弧线
                insertCirclePoint(oneLine.sp, oneLine.tp, edge, 0, crossRadius, points, segments, graphView);
                
                points.add(oneLine.tp);
                segments.add(2);
            });
            
            //线的终点
            points.add(endPoint);
            segments.add(2);
            return {points: points, segments: segments};
        });
    
    
    //
    // edge.type : bundleArc 一组内有多条线时，使用圆弧线来绘制
    // {
    //     // 连线类型
    //     "edge.type":'cross',
    //     // 翻越半径
    //     "edge.cross.radius": 5,
    //     // 翻越线原锚点
    //     "cross.source.anchor": { x: 0.5, y: 0.5 },
    //     // 翻越线目标锚点
    //     "cross.target.anchor": { x: 0.5, y: 0.5 },
    //     // 翻越线，自定义连线折点个数    1p||3p
    //     "cross.edge.type": "3p"  (edge, gap, graphView, sameSourceWithFirstEdge)
    
    
    // }
    ht.Default.setEdgeType("bundleArc", function (edge, gap, graphView, sameSourceWithFirstEdge) {
        var edgeGap             = function (edge, graphView) {
                var edgeType     = edge.s(styleKey_edgeType),
                    edgeIsLooped = edge.isLooped();//是否是自环
            
                if (!edge.getEdgeGroup()) {
                    //当线所属的分组不是0时。或没有分组时
                    return edgeIsLooped
                        ? edge.s(styleKey_edgeGap)
                        : 0;
                }
                var nowEdge     = void 0,
                    gapResult   = 0,
                    totalGap    = 0,
                    lastEdgeGap = 0;//上一条线的gap
                var edgeGroup   = edge.getEdgeGroup();
                var otherGroups = edgeGroup.getSiblings();
                otherGroups.each(function (edgeGroup) {
                    edgeGroup.each(function (oneEdge) {
                        var edgeGap;
                        if (oneEdge._sourceAgent === edge._sourceAgent  //同源的线
                            && oneEdge.s(styleKey_edgeType) == edgeType//类型相同的线
                            && graphView.isVisible(oneEdge) //可见的线
                        )
                        {
                            edgeGap = oneEdge.s(styleKey_edgeGap);
                            if (!nowEdge) {
                                nowEdge = oneEdge;
                            }
                            else {
                                totalGap += lastEdgeGap / 2 + edgeGap / 2;
                            }
                            lastEdgeGap = edgeGap;
                            if (oneEdge === edge) {
                                gapResult = totalGap;
                            }
                        }
                    });
                });
                if (edgeIsLooped) {
                    //自环
                    return totalGap - gapResult + lastEdgeGap;
                }
                else {
                    return gapResult - totalGap / 2;
                }
            }(edge, graphView),
            sourceAgentPosition = edge.getSourceAgent()
                .getPosition(),
            targetAgentPosition = edge.getTargetAgent()
                .getPosition(),
            points              = new ht.List,
            sourceVector        = new ht.Math.Vector2(sourceAgentPosition),
            targetVector        = new ht.Math.Vector2(targetAgentPosition);
        
        var lineVector=sourceVector.add(targetVector);//当前连线的向量
        var middleVector=lineVector.divideScalar(2);//除以2，起点到中间点的向量
        
        new ht.Math.Vector2(sourceAgentPosition).distanceTo(new ht.Math.Vector2(targetAgentPosition));//向量的距离（点乘）
        
        var
            yGap              = targetAgentPosition.y - sourceAgentPosition.y,
            xGap              = targetAgentPosition.x - sourceAgentPosition.x,
            //使用法向量，计算垂直于中心点的距离2edgeGap的位置的点坐标
            yGapOffset        = 2 * edgeGap * -yGap / Math.sqrt(Math.pow(xGap, 2) + Math.pow(yGap, 2)),
            xGapOffset        = 2 * edgeGap * xGap / Math.sqrt(Math.pow(xGap, 2) + Math.pow(yGap, 2)),
            sourceMiddlePoint = {x: middleVector.x + yGapOffset, y: middleVector.y + xGapOffset};
        
        var edgeToCenter = edge.s("edge.center");
        if (edgeToCenter)//连接到中心点
        {
            points.add(sourceAgentPosition);
            points.add(sourceMiddlePoint);
            points.add(targetAgentPosition);
        }
        else {
            var sourceRect  = getNodeRect(graphView, edge.getSourceAgent()),
                targetRect  = getNodeRect(graphView, edge.getTargetAgent());
            var sourcePoint = intersectionLineRect(sourceAgentPosition, sourceMiddlePoint, sourceRect),
                targetPoint = intersectionLineRect(sourceMiddlePoint, targetAgentPosition, targetRect);//线与矩形的交点
            
            points.add({x: sourcePoint[0], y: sourcePoint[1]}),
                points.add(sourceMiddlePoint),
                points.add({
                    x: targetPoint[0],
                    y: targetPoint[1]
                });
        }
        
        return {points: points, segments: new ht.List([1, 3])};
    });
    
    //重置setDataModel的监听函数，重新渲染cross类型的线
    var flag,
        refreshCrossLineFunc                  = (flag = !0, function (t) {
            "*" === t.property || (t = t.data) instanceof ht.Edge && "cross" == t.s("edge.type") && flag && (this.dm()
                .each(function (t) {
                    "cross" == t.s("edge.type") && t.iv();
                }),
                ht.Default.callLater(function () {
                    flag = !0;
                }, null, null, 10));
        }),
        setDataModel_old                      = ht.graph.GraphView.prototype.setDataModel;
    ht.graph.GraphView.prototype.setDataModel = function (newDataModel) {
        var oldDataModel = this._dataModel;
        if (oldDataModel !== newDataModel) {
            if (oldDataModel) {
                oldDataModel.umd(refreshCrossLineFunc, this);//卸载监听
            }
            if (newDataModel) {
                newDataModel.md(refreshCrossLineFunc, this);//注册新的监听
            }
            setDataModel_old.apply(this, arguments);//执行原有的监听
        }
    };
});
